#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/prctl.h>

#include "sample_comm_nnie.h"
#include "sample_media_ai.h"
#include "ai_infer_process.h"
#include "vgs_img.h"
#include "ive_img.h"
#include "posix_help.h"
#include "misc_util.h"
#include "audio_aac_adp.h"
#include "base_interface.h"
#include "osd_img.h"
#include "yolov2_pcb_detect.h"

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif /* End of #ifdef __cplusplus */

//gengguanwei:需要自己更改
#define MODEL_FILE_PCB    "/userdata/models/pcb_detect/pcb_feature.wk" // darknet framework wk model
#define PIRIOD_NUM_MAX     49 // Logs are printed when the number of targets is detected 检测到目标数量时，会打印日志
#define DETECT_OBJ_MAX     32 // detect max obj 检测最多对象

//和训练时的cfg已经保持了一致
#define PCB_FRM_WIDTH     640  //手图像宽度
#define PCB_FRM_HEIGHT    384  //手图像高度

#define DRAW_RETC_THICK    2    // Draw the width of the line 绘制线宽

#define SCORE_MAX           4096    // The score corresponding to the maximum probability 最大概率对应的分数

#define PCB_MIN          30      // Acceptable probability threshold (over this value will be returned to the app) 可接受的概率阈值(超过此值将返回给应用程序)

#define TXT_BEGX            20
#define TXT_BEGY            20

#define MULTIPLE_OF_EXPANSION 100   // Multiple of expansion 膨胀倍数


static IVE_IMAGE_S img; //图像
static DetectObjInfo objs[DETECT_OBJ_MAX] = {0};    //检测到的对象信息

static RectBox boxs[DETECT_OBJ_MAX] = {0};  //所有的框
static RectBox objBoxs[DETECT_OBJ_MAX] = {0};   //对象的框
static RectBox remainingBoxs[DETECT_OBJ_MAX] = {0}; //其余的框

static OsdSet* g_osdsPcb = NULL;
static HI_S32 g_osd0Pcb = -1;

//int uartFd = 0;

//GENG
//模型加载
HI_S32 Yolo2PcbDetectResnetLoad(uintptr_t* model, OsdSet* osds)
{
    SAMPLE_SVP_NNIE_CFG_S *self = NULL;
    HI_S32 ret;

    ret = OsdLibInit();
    HI_ASSERT(ret == HI_SUCCESS);

    g_osdsPcb = osds;
    HI_ASSERT(g_osdsPcb);
    g_osd0Pcb = OsdsCreateRgn(g_osdsPcb); //创建对区域RGN的配置
    HI_ASSERT(g_osd0Pcb >= 0);

    ret = Yolo2Create(&self, MODEL_FILE_PCB);  //创建yolo2
    *model = ret < 0 ? 0 : (uintptr_t)self;
    SAMPLE_PRT("Load pcb detect model, ret:%d\n", ret);

    /* uart open init */
    //uartFd = UartOpenInit();
    //if (uartFd < 0) {
    //    printf("uart1 open failed\r\n");
    //} else {
    //    printf("uart1 open successed\r\n");
    //}

    return ret; //创建模型的结果
}

//GENG
// 模型卸载
HI_S32 Yolo2PcbDetectResnetUnload(uintptr_t model)
{
    Yolo2Destory((SAMPLE_SVP_NNIE_CFG_S*)model); // Uninitialize the hand detection model
    SAMPLE_PRT("Unload pcb detect model success\n");
    OsdsClear(g_osdsPcb);
    return HI_SUCCESS;
}

//PCB检测结果标记
static void PcbDetectFlag(const DetectObjInfo items[], HI_S32 itemNum, HI_CHAR* buf, HI_S32 size)
{
    //根据识别的数字信息进行分类
    HI_S32 offset = 0;
    HI_CHAR *featureName = NULL;

    offset += snprintf_s(buf + offset, size - offset, size - offset - 1, "pcb feature: {");

    for (HI_U32 i = 0; i < itemNum; i++) 
    {
    	const DetectObjInfo *item = &items[i];
    	uint32_t score = (int)(item->score);
        //uint32_t score = item->score * HI_PER_BASE / SCORE_MAX;

        switch (item->cls) 
        {
	        case 0u:
	            featureName = "missing_hole";
	            //UartSendRead(uartFd, PcbMissingHole);
	            SAMPLE_PRT("----feature name----:%s\n", featureName);
	            break;
	        case 1u:
	            featureName = "mouse_bite";
	            //UartSendRead(uartFd, PcbMouseBite);
	            SAMPLE_PRT("----feature name----:%s\n", featureName);
	            break;
	        case 2u:
	            featureName = "open_circuit";
	            //UartSendRead(uartFd, PcbOpenCircuit);
	            SAMPLE_PRT("----feature name----:%s\n", featureName);
	            break;
	        case 3u:
	            featureName = "short";
	            //UartSendRead(uartFd, PcbShort);
	            SAMPLE_PRT("----feature name----:%s\n", featureName);
	            break;
	        case 4u:
	            featureName = "spur";
	            //UartSendRead(uartFd, PcbSpur);
	            SAMPLE_PRT("----feature name----:%s\n", featureName);
	            break;
	        case 5u:
	            featureName = "spurious_copper";
	            //UartSendRead(uartFd, PcbSpuriousCopper);
	            SAMPLE_PRT("----feature name----:%s\n", featureName);
	            break;
	        default:
	            featureName = "others";
	            //UartSendRead(uartFd, InvalidFeature);
	            SAMPLE_PRT("----feature name----:%s\n", featureName);
	            break;
    	}

    	offset += snprintf_s(buf + offset, size - offset, size - offset - 1, "%s%s %u:%u%%", (i == 0 ? " " : ", "), featureName, (int)item->cls, (int)score);
        HI_ASSERT(offset < size);
    }
    //SAMPLE_PRT("----pcb feature item flag----num:%d, score:%d\n", item->cls, score);
	offset += snprintf_s(buf + offset, size - offset, size - offset - 1, " }");
    HI_ASSERT(offset < size);
}

//输入图像进行模型分析
HI_S32 Yolo2PcbDetectResnetCal(uintptr_t model, VIDEO_FRAME_INFO_S *srcFrm, VIDEO_FRAME_INFO_S *resFrm)
{
    SAMPLE_PRT("begin Yolo2PcbDetectResnetCal\n");

    SAMPLE_SVP_NNIE_CFG_S *self = (SAMPLE_SVP_NNIE_CFG_S*)model;

    //定义要在屏幕显示的内容
    static HI_CHAR prevOsd[NORM_BUF_SIZE] = ""; //OSD (On-Screen Display) 是在屏幕上直接显示使用者所调整的结果和设定的内容
    HI_CHAR osdBuf[NORM_BUF_SIZE] = "";

    HI_S32 ret;
    int objNum;
    int num = 0;

    ret = FrmToOrigImg((VIDEO_FRAME_INFO_S*)srcFrm, &img); //视频帧到ive图像。复制数据指针，不复制数据。
    SAMPLE_CHECK_EXPR_RET(ret != HI_SUCCESS, ret, "pcb detect for YUV Frm to Img FAIL, ret=%#x\n", ret);

    ret = Yolo2CalImg(self, &img, objs, DETECT_OBJ_MAX, &objNum);
    if (ret < 0) 
    {
        SAMPLE_PRT("Pcb detect Yolo2CalImg FAIL, for cal FAIL, ret:%d\n", ret);
        return ret;
    }

    //对每一个检测到的对象
    for (int i = 0; i < objNum; i++) 
    {
        RectBox *box = &objs[i].box;    //获取所有矩形框
        //按比例转换坐标
        RectBoxTran(box, PCB_FRM_WIDTH, PCB_FRM_HEIGHT, resFrm->stVFrame.u32Width, resFrm->stVFrame.u32Height);
        SAMPLE_PRT("yolo2_out: {%d, %d, %d, %d}\n", box->xmin, box->ymin, box->xmax, box->ymax);
        boxs[i] = *box; //获得所有的框
    }

    if(objNum > 0)
    {
        objBoxs[0] = boxs[0];
        MppFrmDrawRects(resFrm, objBoxs, 1, RGB888_RED, DRAW_RETC_THICK); // Target hand objnum is equal to 1

        for (int j = 1; (j < objNum) && (objNum > 1); j++) 
        {
            remainingBoxs[num++] = boxs[j]; //获取其他的所有框
            MppFrmDrawRects(resFrm, remainingBoxs, objNum - 1, RGB888_RED, DRAW_RETC_THICK);
        }
    }
    
    PcbDetectFlag(objs, objNum, osdBuf, sizeof(osdBuf));

    //两个字符串数组结果不一致
    if (strcmp(osdBuf, prevOsd) != 0) {
        HiStrxfrm(prevOsd, osdBuf, sizeof(prevOsd)); //写完整的信息到prevOsd

        // Superimpose graphics into resFrm 将图形叠加到resFrm中，即将图形叠加至结果帧中
        HI_OSD_ATTR_S rgn;
        //设置文本区域的属性值
        TxtRgnInit(&rgn, osdBuf, TXT_BEGX, TXT_BEGY, ARGB1555_YELLOW2); // font width and heigt use default 40 
        OsdsSetRgn(g_osdsPcb, g_osd0Pcb, &rgn); //为OsdSet中的指定区域设置属性
        ret = HI_MPI_VPSS_SendFrame(0, 0, srcFrm, 0);
        if (ret != HI_SUCCESS) 
        {
            SAMPLE_PRT("Error(%#x), HI_MPI_VPSS_SendFrame failed!\n", ret);
        }
    }

    return ret;
}

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* End of #ifdef __cplusplus */










